Factory Pattern

2 min read
Foundational2 min read
Rapid overview

Factory Pattern

TL;DR

Centralize creation of objects (like API handlers or executors) based on configuration or environment.

How it works


🧩 Example — Trading platform executor factory

public interface ITradeExecutor
{
    void Execute(Order order);
}

public class Mt4Executor : ITradeExecutor
{
    public void Execute(Order order) =>
        Console.WriteLine($"[MT4] Executing {order.Symbol}");
}

public class Mt5Executor : ITradeExecutor
{
    public void Execute(Order order) =>
        Console.WriteLine($"[MT5] Executing {order.Symbol}");
}

public static class ExecutorFactory
{
    public static ITradeExecutor Create(string platform) => platform switch
    {
        "MT4" => new Mt4Executor(),
        "MT5" => new Mt5Executor(),
        _ => throw new ArgumentException("Unknown platform")
    };
}

// --- Usage ---
var platform = "MT5";
var executor = ExecutorFactory.Create(platform);
executor.Execute(new Order { Symbol = "USDJPY", Amount = 5000 });

✅ Why it matters:

  • Simplifies platform switching (MT4, MT5, FIX, cTrader).
  • New platforms require no refactor — just a new class and switch entry.

Quick recall Q&A

Q: When should you use a Factory pattern in .NET services? A: When object creation depends on runtime context (config, tenant, instrument) and you want to isolate creation logic from consumers. Factories prevent scattered new calls and keep construction consistent.
Q: How does DI interact with factories? A: You can register implementations in DI and inject Func<Key, ITradeExecutor> or an IExecutorFactory that resolves services by key. This keeps factories composable with scoped dependencies.
Q: How do you avoid giant switch statements as platforms grow? A: Use a dictionary of delegates, reflection-based registration, or DI IServiceProvider lookups keyed by platform. Alternatively, combine with the Strategy pattern so each executor registers itself.
Q: What’s the benefit of abstract factories? A: When you need to create families of related objects (e.g., executor + validator + serializer per platform), an abstract factory ensures compatible components are produced together.
Q: How do factories aid testing? A: Tests can inject fake factories returning mock executors, isolating code under test without hitting real integrations. It also simplifies verifying that the correct executor is chosen for a scenario.
Q: How do you handle configuration-driven factories? A: Load mappings from configuration or feature flags, then let the factory instantiate types via DI. This enables runtime toggles (e.g., route VIP tenants to a premium executor) without code changes.
Q: When is the factory pattern overkill? A: When only two concrete types exist and creation logic is trivial. Start simple, and introduce a factory once switching logic repeats or needs shared validation/logging.
Q: How do you ensure factories remain SRP-compliant? A: Keep them focused on creation. Any orchestration, validation, or logging should be delegated to other components or decorators so factories don't become god objects.
Q: Can factories return async results? A: Yes—define methods returning Task<T> if creation involves I/O (e.g., pulling secrets). Just ensure callers understand the lifecycle and avoid blocking .Result.
Q: How do you register factories in DI? A: Register each concrete type and a factory delegate: services.AddTransient<ExecutorFactory>(); or services.AddSingleton<Func<string, ITradeExecutor>>(provider => key => provider.GetRequiredKeyedService<ITradeExecutor>(key));.

See also